package com.gitee.elf.core.entity

import com.gitee.elf.api.event.DataFromConfigSerializerEvent
import com.gitee.elf.core.data.*
import com.gitee.elf.core.data.DataColor.Companion.toColor
import com.gitee.elf.core.data.DataDyeColor.Companion.toDyeColor
import com.gitee.elf.core.data.Serializer
import org.bukkit.Color
import org.bukkit.DyeColor
import org.bukkit.Location
import org.bukkit.inventory.ItemStack
import taboolib.library.configuration.ConfigurationSection
import taboolib.library.xseries.getItemStack
import taboolib.module.configuration.util.getLocation
import java.util.concurrent.ConcurrentHashMap

class MetadataContainer(val instance: EntityInstance) {

    private val table = ConcurrentHashMap<String, Data>()

    inline fun <reified T> getValue(id: String): T? {
        val data = get(id) ?: return null
        return data.bind as T
    }

    fun get(id: String): Data? {
        val data = table[id] ?: return null
        // 及时删除
        if (!data.isValid) {
            this.remove(id)
            return null
        }

        return data
    }

    operator fun set(id: String, value: Data) {
        table[id] = value
        instance.metaUpdate(id, value)
    }

    fun remove(id: String) {
        table.remove(id)
    }

    fun clear() {
        table.clear()
    }

    fun unchange() {
        table.values.forEach {
            it.change = false
        }
    }

    fun deepMerge(dataContainer: MetadataContainer) {
        dataContainer.table.forEach {
            this[it.key] = it.value.clone()
        }
    }

    fun deepClone(): MetadataContainer {
        val container = MetadataContainer(instance)
        this.table.forEach {
            container[it.key] = it.value.clone()
        }
        return container
    }

    fun map(): Map<String, Any> {
        return table.map { it.key to it.value.bind.getValue() }.toMap()
    }

    companion object {

        fun serialize(value: Any): com.gitee.elf.core.data.Data {
            return when (value) {

                is com.gitee.elf.core.data.Data -> value

                is Int -> DataInt(value)
                is String -> DataString(value)
                is Double -> DataDouble(value)
                is Boolean -> DataBoolean(value)
                is List<*> -> DataList().let { data ->
                    value.forEach { data += serialize(it!!) }
                    data
                }

                is ItemStack -> DataItemStack(value)
                is Color -> DataColor(value)
                is DyeColor -> DataDyeColor(value)
                is Location -> DataLocation(value)
                is Map<*, *> -> DataObject().let { data ->
                    value.forEach { (key, value) ->
                        data[key.toString()] = serialize(value!!)
                    }
                    data
                }

                else -> error("${value::class.java} type serialize not supported.")
            }
        }

        fun Any.safeData(health: Long = -1): Data {
            return Data(serialize(this), health)
        }

    }

    class Data(var bind: com.gitee.elf.core.data.Data, val health: Long) {

        val markCreate = System.currentTimeMillis()

        var change = true

        val isValid: Boolean
            get() = health == -1L || System.currentTimeMillis() > markCreate + health

        fun toJson(): String {
            return Serializer.toJson(bind)
        }

        fun clone(): Data {
            return Data(bind.clone(), health)
        }

    }

}